package np13;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import np13.earth.Funafitu;
import np13.earth.HomeLand;
import np13.ferry.Ferry;
import np13.ferry.Pier;
import np13.monads.FunafituMonad;
import np13.monads.HomeLandMonad;
import np13.sddb.SDDB;
import np13.utils.TimeTest;

/**
 * Main class where the representation of our world is stored. All
 * initializations are done here and any component can be reached from here (use
 * getInstance()).
 */
public class World {

	/**
	 * The one and only World instance.
	 */
	private final static World instance = new World();

	/**
	 * @return The world instance.
	 */
	public static World getInstance() {
		return instance;
	}

	/**
	 * Starts Snowden and all monads. Additionally, the SDDB is started.
	 * 
	 * @param args
	 *            The arguments passed to the program, which will have no effect
	 *            whatsoever.
	 */
	public static void main(String[] args) {
		// start Snowden and SDDB
		World.getInstance().getSnowden().start();
		World.getInstance().getsDDB().start();
		// start Monads
		for (final Smell smell : Smell.values()) {
			// erstelle pro geruch zwei monaden
			World.getInstance().funafituMonad.put(smell, new FunafituMonad(
					smell));
			World.getInstance().homeLandMonad.put(smell, new HomeLandMonad(
					smell));
		}
		// starte die Monaden
		for (final Smell smell : Smell.values()) {
			World.getInstance().funafituMonad.get(smell).start();
			World.getInstance().homeLandMonad.get(smell).start();
		}
	}

	/**
	 * The Funafitu atoll.
	 */
	private final Funafitu funafitu;

	/**
	 * A homeland per smell.
	 */
	private final Map<Smell, HomeLand> homeLands;

	/**
	 * A Funafitu monad for each smell.
	 */
	private final Map<Smell, FunafituMonad> funafituMonad;

	/**
	 * A homeland monad for each smell.
	 */
	private final Map<Smell, HomeLandMonad> homeLandMonad;

	/**
	 * The database.
	 */
	private final SDDB sDDB;

	/**
	 * The one and only snowden.
	 */
	private final Snowden snowden;

	/**
	 * Initializes all components. Nothing will be started yet.
	 */
	private World() {
		TimeTest.isEnabled = true;
		TimeTest.start("EVA Run-Time:");
		Logger.log("Beginning initialization.");
		Runtime runtime = Runtime.getRuntime();
		int numberProcessors = runtime.availableProcessors();
		System.out.println("Number of processors:" + numberProcessors);

		final LinkedList<Slime> slimes = new LinkedList<Slime>();
		homeLands = new HashMap<Smell, HomeLand>();
		funafituMonad = new HashMap<Smell, FunafituMonad>();
		homeLandMonad = new HashMap<Smell, HomeLandMonad>();
		final Map<Smell, SlimeQueue> slimeQueues = new HashMap<Smell, SlimeQueue>();

		for (final Smell smell : Smell.values()) {
			final LinkedList<Slime> slimesBornHere = new LinkedList<Slime>();

			for (int i = 0; i < Configuration.SLIME_COUNT_PER_HOMELAND; i++) {
				final Slime newSlime = new Slime();
				slimes.add(newSlime);
				slimesBornHere.add(newSlime);
			}

			System.out.println("Number of created " + smell.name()
					+ " Slimes :" + "[" + slimesBornHere.size() + "]");

			final Pier centralPier = new Pier();// Funafitu
			final Pier homelandPier = new Pier();// homeland

			final Ferry ferry = new Ferry(smell, homelandPier, centralPier,
					Configuration.getFerrySize(smell));

			ferry.sail(); // Ferry starts sailing to Funafitu

			final HomeLand homeLand = new HomeLand(homelandPier, slimesBornHere);
			homeLands.put(smell, homeLand);

			final SlimeQueue slimeQueue = new SlimeQueue(centralPier,
					Configuration.getFerrySize(smell));
			slimeQueues.put(smell, slimeQueue);
		}

		Collections.shuffle(slimes); // shuffle to get a random distribution
		funafitu = new Funafitu(slimeQueues, slimes);

		sDDB = new SDDB();

		snowden = new Snowden();
		System.out.println("initialization finished");
	}

	/**
	 * @return The Funafitu.
	 */
	public Funafitu getFunafitu() {
		return funafitu;
	}

	/**
	 * @return All Funafitu habitats as a HashMap. Use get(smell) to get a
	 *         specific monad.
	 */
	public Map<Smell, FunafituMonad> getFunafituMonad() {
		return funafituMonad;
	}

	/**
	 * 
	 * @return All Home Land habitats as a HashMap. Use get(smell) to get a
	 *         specific monad.
	 */
	public Map<Smell, HomeLandMonad> getHLMonad() {
		return homeLandMonad;
	}

	/**
	 * @return All homelands as a HashMape. Use get(smell) to get a specific
	 *         homeland.
	 */
	public Map<Smell, HomeLand> getHomeLands() {
		return homeLands;
	}

	/**
	 * @return The SDDB.
	 */
	public SDDB getsDDB() {
		return sDDB;
	}

	/**
	 * @return The one and only snowden.
	 */
	public Snowden getSnowden() {
		return snowden;
	}
}
